-
Notifications
You must be signed in to change notification settings - Fork 984
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
improve bin-packing performance #652
Conversation
✔️ Deploy Preview for karpenter-docs-prod canceled. 🔨 Explore the source changes: 03613d2 🔍 Inspect the deploy log: https://app.netlify.com/sites/karpenter-docs-prod/deploys/612fc330d1a63300080bd7ee |
pkg/packing/packable.go
Outdated
} | ||
return result | ||
} | ||
|
||
// isFull checks if the reserved resources + the minimum resourced pod overflows the total resources available. | ||
func (p *Packable) isFull(minPod *v1.Pod) bool { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any reason you can't shared logic with
func (p *Packable) reservePod(pod *v1.Pod) bool {
requests := resources.RequestsForPods(pod)
requests[v1.ResourcePods] = *resource.NewQuantity(1, resource.BinarySI)
return p.reserve(requests)
}
func (p *Packable) reserve(requests v1.ResourceList) bool {
candidate := resources.Merge(p.reserved, requests)
// If any candidate resource exceeds total, fail to reserve
for resourceName, quantity := range candidate {
if quantity.Cmp(p.total[resourceName]) > 0 {
return false
}
}
p.reserved = candidate
return true
}
As far as I understand it, this function does exactly what you need, you'd just need a dry-run mode or something.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, you're probably right that this can be reduced. Let me try to merge these. I think the dry-run would be the isFull and the reserve would still exist but do less and just call isFull and the reserve.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, after digging into trying to merge these two or use each other, I think it's actually making the interface of reserve significantly worse off. The reserve function is doing something slightly different and returning a different result.
The reserve function will take a resource list and reserve on the node if it doesn't overflow the node's resource. The return value signifies that the reservation succeeded (did not overflow) or failed (overflowed).
The isFull function is only concerned about the node totals and seeing they are all the way full, but not overflowed OR would overflow with the smallest pod that needs to be reserved in the list.
I think merging these functions makes it less readable, adds complexity to reserve, and the return value gets muddied since we'd need two bools returns, one indicating if the reservation succeeded and one noting if the node is full.
pkg/packing/packable.go
Outdated
@@ -100,10 +101,29 @@ func (p *Packable) Pack(pods []*v1.Pod) *Result { | |||
return result | |||
} | |||
result.unpacked = append(result.unpacked, pod) | |||
|
|||
if p.isFull(pods[len(pods)-1]) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it possible to move this check to the beginning of the loop? Unless I'm misunderstanding.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it can be moved to the top. I think it might make more sense to move after the reserve block though.
Here's how I reason in english about the func:
Try to reserve the space for the pod, if that fails, then check if the node is full and potentially exit the loop. If the node isn't full (next block) and nothing has been packed (meaning the largest pod was unable to be packed), exit the loop because we need to find a bigger node. If we've made progress, but the current node is just getting full, add the items to the unpacked list and keep trying smaller pods.
Nice! |
Amazing! |
Issue, if available:
#625
Description of changes:
Increased performance of bin-packing. This specific optimization found a loop where a terminable case was not being checked which allowed a significant number of loop iterations to be skipped.
The terminable case optimization in the packing loop had a 30x improvement
The smaller "min-pod" optimization resulted in a 50% improvement
Benchmarks:
Testing:
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.